//////////////////////////////////////////////////////////////////////////
//
// This file is a part of UKROP project.
// Copyright (c) Vladimir Gumenuk
//
// PROJECT:         Xort Custom Debug Engine
// FILE:            
// AUTHOR:          Vladimir Gumenuk
// DATE:            Dec-2012 -- Jan-2013
// MODIFICATIONS:
//
//////////////////////////////////////////////////////////////////////////

#pragma once

//////////////////////////////////////////////////////////////////////////
// base class for events implementation
class CEventBase : public IDebugEvent2
{
public:
    CEventBase( REFIID iid, DWORD dwAttrib );
    virtual ~CEventBase();

    //////////////////////////////////////////////////////////// 
    // IUnknown methods 
    STDMETHOD_(ULONG, AddRef)(); 
    STDMETHOD_(ULONG, Release)(); 
    STDMETHOD(QueryInterface)(REFIID riid, LPVOID *ppvObj); 

    //////////////////////////////////////////////////////////// 
    // IDebugEvent2 methods 
    STDMETHOD(GetAttributes)(DWORD* pdwAttrib); 

    // helper: sends an event back to the session debug manager (SDM)
    STDMETHOD(Send)();

protected:
    DWORD   m_dwAttrib;
    IID     m_riid;
    ULONG   m_iRefCount;
};

#define DECLARE_IUNKNOWN \
    STDMETHOD_(ULONG, AddRef)(); \
    STDMETHOD_(ULONG, Release)(); \
    STDMETHOD(QueryInterface)(REFIID riid, LPVOID *ppvObj);

//////////////////////////////////////////////////////////////////////////
// The first event sent by a debug engine is the debug engine create event, 
// represented by the IDebugEngineCreateEvent2 interface. 
// This event lets the session debug manager know that the debug engine is ready.
class CEngineCreateEvent :
    public IDebugEngineCreateEvent2, 
    public CEventBase 
{
public:
    CEngineCreateEvent( IDebugEngine2 *pEngine );
    virtual ~CEngineCreateEvent();

    DECLARE_IUNKNOWN

    //////////////////////////////////////////////////////////// 
    // IDebugEngineCreateEvent2 methods 
    STDMETHOD(GetEngine)(IDebugEngine2 **ppEngine); 

private:
    CComPtr<IDebugEngine2>      m_spEngine;
};

//////////////////////////////////////////////////////////////////////////
// sends to SDM when process is created
class CProcessCreateEvent :
    public IDebugProcessCreateEvent2,
    public CEventBase
{
public:
    CProcessCreateEvent();
    virtual ~CProcessCreateEvent();

    DECLARE_IUNKNOWN
};

class CProcessDestroyEvent :
    public IDebugProcessDestroyEvent2,
    public CEventBase
{
public:
    CProcessDestroyEvent();
    virtual ~CProcessDestroyEvent();

    DECLARE_IUNKNOWN
};

//////////////////////////////////////////////////////////////////////////
// The second event typically sent to the session debug manager (SDM) 
// is the program creation event, represented by the IDebugProgramCreateEvent2 interface. 
// This event tells the SDM that the program is ready to be debugged.
class CProgramCreateEvent :
    public IDebugProgramCreateEvent2, 
    public CEventBase 
{
public:
    CProgramCreateEvent();
    virtual ~CProgramCreateEvent();
    DECLARE_IUNKNOWN
};

//////////////////////////////////////////////////////////////////////////
// Once the loading of the program has completed (but not actually started), 
// the SDM needs to be notified. That is why the debug engine sends the 
// IDebugLoadCompleteEvent2 to the SDM. Event is synchronous.
class CLoadCompleteEvent :
    public IDebugLoadCompleteEvent2, 
    public CEventBase 
{ 
public:
    CLoadCompleteEvent() : CEventBase( IID_IDebugLoadCompleteEvent2, EVENT_IMMEDIATE ) { ; }//EVENT_SYNC_STOP )
    virtual ~CLoadCompleteEvent() { ; }
    DECLARE_IUNKNOWN
};

//////////////////////////////////////////////////////////////////////////
// When the program has started and reached its entry point, 
// the debug engine sends the IDebugEntryPointEvent2 event to the SDM
// Event is synchronous.
class CEntryPointEvent :
    public IDebugEntryPointEvent2, 
    public CEventBase 
{ 
public:
    CEntryPointEvent() : CEventBase( IID_IDebugEntryPointEvent2, EVENT_IMMEDIATE  ) { ; }
    virtual ~CEntryPointEvent() { ; }
    DECLARE_IUNKNOWN
};

//////////////////////////////////////////////////////////////////////////
// After the SDM calls IDebugPendingBreakpoint2::Bind, the debug engine responds 
// by sending to the SDM either an IDebugBreakpointBoundEvent2 (if the pending breakpoint can be bound)
// or an IDebugBreakpointErrorEvent2 (if the pending breakpoint cannot be bound).
class CBreakpointBoundEvent :
    public IDebugBreakpointBoundEvent2, 
    public CEventBase 
{
public:
    CBreakpointBoundEvent( IEnumDebugBoundBreakpoints2 *pEnum, IDebugPendingBreakpoint2 *pPending ); 
    virtual ~CBreakpointBoundEvent(){ ; }
    DECLARE_IUNKNOWN 
    //////////////////////////////////////////////////////////// 
    //IDebugBreakpointBoundEvent2 methods 
    STDMETHOD(GetPendingBreakpoint)(IDebugPendingBreakpoint2** ppPendingBP); 
    STDMETHOD(EnumBoundBreakpoints)(IEnumDebugBoundBreakpoints2** ppEnum); 
private:
    CComPtr<IEnumDebugBoundBreakpoints2>    m_spEnum;
    CComPtr<IDebugPendingBreakpoint2>       m_spPending;
};

//////////////////////////////////////////////////////////////////////////
// Sends to SDM when breakpoint is reached
class CCodeBreakpointEvent :
    public IDebugBreakpointEvent2, 
    public CEventBase 
{
public:
    CCodeBreakpointEvent( IDebugBoundBreakpoint2 *pBP ) : CEventBase( IID_IDebugBreakpointEvent2, EVENT_SYNCHRONOUS|EVENT_STOPPING ) { m_spBP = pBP; }
    virtual ~CCodeBreakpointEvent() { ; }
    DECLARE_IUNKNOWN 
    STDMETHOD(EnumBreakpoints)(IEnumDebugBoundBreakpoints2 **ppEnum); 
private:
    CComPtr<IDebugBoundBreakpoint2>     m_spBP;
};

//////////////////////////////////////////////////////////////////////////
// Sent by the debug engine to the session debug manager when the program being debugged is terminated.
class CProgramDestroyEvent :
    public IDebugProgramDestroyEvent2, 
    public CEventBase 
{
public:
    CProgramDestroyEvent(DWORD dwExitCode) : CEventBase(IID_IDebugProgramDestroyEvent2, EVENT_IMMEDIATE ) { m_dwExitCode = dwExitCode; }
    virtual ~CProgramDestroyEvent() { ; }
    DECLARE_IUNKNOWN 
    STDMETHOD(GetExitCode)(DWORD *pdwExit) {
        *pdwExit = m_dwExitCode; 
        return S_OK; 
    }

private:
    DWORD   m_dwExitCode;
};

//////////////////////////////////////////////////////////////////////////
//
class CDebugBreakEvent : 
    public IDebugBreakEvent2, 
    public CEventBase
{
public:
    CDebugBreakEvent() : CEventBase( IID_IDebugBreakEvent2, EVENT_SYNCHRONOUS ) { ; }
    virtual ~CDebugBreakEvent() { ; }
    DECLARE_IUNKNOWN
};

//////////////////////////////////////////////////////////////////////////
//
class COutputStringEvent :
    public IDebugOutputStringEvent2,
    public CEventBase
{
public:
    COutputStringEvent( LPCSTR lpszMessage ) : CEventBase( IID_IDebugOutputStringEvent2, EVENT_ASYNCHRONOUS ) { 
        m_bsString = CComBSTR( lpszMessage );
    }
    virtual ~COutputStringEvent() { ; }
    DECLARE_IUNKNOWN

    STDMETHOD(GetString)( BSTR *pbstrString ) {
        *pbstrString = SysAllocString( m_bsString );
        return S_OK;
    }
private:
    CComBSTR m_bsString;
};

//////////////////////////////////////////////////////////////////////////
//
class CActivateDocumentEvent : 
    public IDebugActivateDocumentEvent2,
    public CEventBase
{
public:
    CActivateDocumentEvent( IDebugDocument2 *pDoc ) 
        : CEventBase( IID_IDebugActivateDocumentEvent2, EVENT_IMMEDIATE ) {  
        m_spDoc = pDoc;
    }
    virtual ~CActivateDocumentEvent() { ; }
    DECLARE_IUNKNOWN

    STDMETHOD(GetDocument)( IDebugDocument2 **ppDoc ) {
        *ppDoc = m_spDoc;
        (*ppDoc)->AddRef();
        return S_OK;
    }

    STDMETHOD(GetDocumentContext)( IDebugDocumentContext2 **ppDocContext ) {
        return m_spDoc->QueryInterface( IID_IDebugDocumentContext2, (void**)ppDocContext );
    }

private:
    CComPtr<IDebugDocument2>            m_spDoc;
};

//////////////////////////////////////////////////////////////////////////
// sends each time single step execution complete
class CStepCompleteEvent : 
    public IDebugStepCompleteEvent2,
    public CEventBase
{
public:
    CStepCompleteEvent() : CEventBase( IID_IDebugStepCompleteEvent2, EVENT_SYNCHRONOUS | EVENT_STOPPING ) { ; }
    virtual ~CStepCompleteEvent() { ; }
    DECLARE_IUNKNOWN
};

//////////////////////////////////////////////////////////////////////////
//
class CCanStopEvent :
    public IDebugCanStopEvent2,
    public CEventBase
{
public:
    CCanStopEvent( CANSTOP_REASON Reason, IDebugDocumentContext2 *pDocCtx, IXProgram *pProg ) 
        : CEventBase( IID_IDebugCanStopEvent2, EVENT_SYNCHRONOUS ) {
        m_Reason = Reason; 
        m_spDocCtx = pDocCtx;
        m_spXProg = pProg;
    }
    virtual ~CCanStopEvent() { ; }

DECLARE_IUNKNOWN

    STDMETHOD(GetReason)( CANSTOP_REASON *pcr ) {
        *pcr = m_Reason;
        return S_OK;
    }

    STDMETHOD(CanStop)( BOOL fCanStop ) {
        return m_spXProg->CanStop( fCanStop );
    }

    STDMETHOD(GetDocumentContext)( IDebugDocumentContext2 **ppDocCxt ) {
        return m_spDocCtx->QueryInterface( IID_IDebugDocumentContext2, (void**)ppDocCxt );
    }

    STDMETHOD(GetCodeContext)( IDebugCodeContext2 **ppCodeContext ) {
        return m_spDocCtx->QueryInterface( IID_IDebugCodeContext2, (void**)ppCodeContext );
    }

private:
    CComPtr<IDebugDocumentContext2> m_spDocCtx;
    CComPtr<IXProgram>              m_spXProg;
    CANSTOP_REASON                  m_Reason;
};


class CExpressionEvaluationCompleteEvent :
    public IDebugExpressionEvaluationCompleteEvent2,
    public CEventBase
{
public:
    CExpressionEvaluationCompleteEvent( IDebugExpression2 *pExpr, IDebugProperty2 *pProp )
        : CEventBase( IID_IDebugExpressionEvaluationCompleteEvent2, EVENT_SYNCHRONOUS ) {
        m_spExpr = pExpr;
        m_spProp = pProp;
    }
    virtual ~CExpressionEvaluationCompleteEvent() { ; }
DECLARE_IUNKNOWN

    STDMETHOD(GetExpression)( IDebugExpression2 **ppExpr ) {
        return m_spExpr->QueryInterface( IID_IDebugExpression2, (void**)ppExpr );
    }

    STDMETHOD(GetResult)( IDebugProperty2 **ppResult ) {
        return m_spProp->QueryInterface( IID_IDebugProperty2, (void**)ppResult );
    }

private:
    CComPtr<IDebugExpression2> m_spExpr;
    CComPtr<IDebugProperty2>   m_spProp;
};
